package com.da.auth.security.filter;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.da.auth.entity.UserInfo;
import com.da.auth.entity.constant.DailyAuthConstant;
import com.da.auth.entity.vo.UserDetailsVO;
import com.da.auth.security.config.LoginFailureHandler;
import com.da.auth.security.jwt.BaseContext;
import com.da.auth.security.jwt.JwtClaimsConstant;
import com.da.auth.security.jwt.JwtUtils;
import com.da.common.exception.UserAuthenticationException;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;


/**
 * @Author: hyacinth
 * @CreateTime: 2025-02-10
 * @Version: 1.0
 * <p>
 * JWT Token 过滤器，用于在每次请求时验证 JWT Token
 * 该过滤器继承自 {@link OncePerRequestFilter}，确保每个请求只被过滤一次
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenOncePerRequestFilter extends OncePerRequestFilter {


    private final LoginFailureHandler loginFailureHandler;

    private final JwtUtils jwtUtils;






    /**
     * 处理每个请求，验证 JWT Token。
     *
     * @param request     HTTP 请求对象
     * @param response    HTTP 响应对象
     * @param filterChain 过滤器链
     * @throws ServletException 如果发生 Servlet 异常
     * @throws IOException      如果发生 I/O 异常
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info("<==========进入了JwtTokenOncePerRequestFilter.doFilterInternal==========>");
        // 1. 判断当前请求是否需要验证 JWT Token
        boolean shouldValidateToken = shouldValidateToken(request);
        if (!shouldValidateToken) {
            // 2. 校验token
            try {
                validateToken(request);
                log.info("Token 校验成功");
            } catch (AuthenticationException e) {
                // 处理认证异常
                log.error("Token 校验失败: {}", e.getMessage());
                loginFailureHandler.onAuthenticationFailure(request, response, e);
                return;
            }
        }
        // 3. 继续执行过滤器链
        try {
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            log.error("过滤器链执行失败: {}", e.getMessage(), e);
            throw e;
        }
    }

    /**
     * 判断当前请求是否需要验证 JWT Token
     *
     * @param request 请求
     * @return
     */
    private boolean shouldValidateToken(HttpServletRequest request) {
        log.info("<==========进入了JwtTokenOncePerRequestFilter.shouldValidateToken==========>");
        // 获取当前请求路径
        String uri = request.getRequestURI();
        // 根据 SecurityConfig 中的配置判断是否需要验证 JWT Token
        return isPermittedPath(uri);
    }

    /**
     * 判断当前请求路径是否是直接放行的路径
     *
     * @param uri 请求路径
     * @return
     */
    private boolean isPermittedPath(String uri) {
        log.info("<==========进入了JwtTokenOncePerRequestFilter.isPermittedPath==========>");
        // 根据 SecurityConfig 中的配置判断当前请求路径是否是直接放行的路径
        return uri.startsWith("/dl/") || uri.startsWith("/user/createUser") || uri.startsWith("/dailyGoals/");
    }


    /**
     * /**
     * 校验token
     *
     * @param request 请求
     * @throws UserAuthenticationException
     */
    private void validateToken(HttpServletRequest request) throws UserAuthenticationException {
        log.info("<==========进入了JwtTokenOncePerRequestFilter.validateToken==========>");
        // 说明：登录了，再次请求其他需要认证的资源
        String token = request.getHeader(DailyAuthConstant.TOKEN_HEADER);
        if (ObjectUtils.isEmpty(token)) { // header没有token
            token = request.getParameter(DailyAuthConstant.TOKEN_HEADER);
        }
        if (ObjectUtils.isEmpty(token)) {
            log.error("请求中未包含有效的 Token");
            throw new UserAuthenticationException("token为空");
        }
        // 校验token
        UserDetailsVO userDetailsVO;
        try {
            log.info("jwt校验: {}", token);
            Claims claims = jwtUtils.parseJWT(token);
            if (claims == null) {
                log.error("JWT 解析失败，Claims 为空");
                throw new UserAuthenticationException("token校验失败");
            }
            Object userInfoObj = claims.get(JwtClaimsConstant.USERINFO_LOGIN);
            if (userInfoObj == null) {
                log.error("JWT 中未包含 {} 声明", JwtClaimsConstant.USERINFO_LOGIN);
                throw new UserAuthenticationException("token校验失败");
            }
            String loginUserString = userInfoObj.toString();
            // 把json字符串转为对象
            userDetailsVO = JSON.parseObject(loginUserString, UserDetailsVO.class);
            if (userDetailsVO == null) {
                log.error("JSON 解析失败，无法将字符串转换为 UserDetailsVO 对象");
                throw new UserAuthenticationException("token校验失败");
            }
            log.info("当前用户id：{}", userDetailsVO.getId());
            BaseContext.setCurrentId(userDetailsVO.getId());
        } catch (Exception ex) {
            log.error("Token 校验过程中出现异常: {}", ex.getMessage(), ex);
            throw new UserAuthenticationException("token校验失败");
        }
        // 把校验后的用户信息再次放入到SpringSecurity的上下文中
        UserInfo info = new UserInfo();
        info.setUsername(userDetailsVO.getUsername());
        info.setPassword(userDetailsVO.getPassword());
        info.setRoles(userDetailsVO.getRoles());
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetailsVO, null, info.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }
}